I will talk about the new scheduler API, to manage and schedule tasks or jobs, present in Windows 8.1. This allows us to set the priority of each job so that system resources are more effectively used and responsiveness is improved. The Scheduler API forms part of WinJS.Utilities and is brand new.
Asynchronous Programming
Here is a small quote from it: "Asynchrony is crucial to any app that depends on certain tasks that may cause bottlenecks or block the rest of your program, better yet, freezing. So in fact, it makes applications respond quicker while running several tasks in the background."Now that we know what Asynchronous programming is all about, let us move on to the new Scheduler API in Windows 8.1.
Scheduler API
The Scheduler API consolidates all work queues into a single queue with priority-based scheduling capabilities. This provides faster and more fluid applications. More details of the Scheduler API can be foundhere.
Our Project
Open Visual Studio 2013 Preview on Windows 8.1 Preview or Visual Studio 2013 RC on Windows 8.1 RTM, and start a new blank JavaScript Windows Store project entitled Schedule_JS.
"Wait! A JavaScript project? Hannes, are you sure it is not VB?"
"Yes, I am"
"But why?"
"Well, with this article I am trying to explain the Scheduler API, which forms part of WinJS.Utilities, and, it's time for something different concerning the Windows 8 Store."
Any other questions, No, right.
DesignAdd four buttons to your default.html web page that was created automatically via the toolbox, or you could edit the HTML code directly. Your design should resemble Figure 1, and your code should resemble the following code segment.
default.html Body code :
- <body>
- <p>Scheduling Tasks</p>
- <button id="pause">Pause Tasks</button>
- <button id="cancel">Cancel Tasks</button>
- <button id="yield">Yield Job Within Task</button>
- <button id="drain">Drain Tasks</button>
- </body>
Coding
Now that the design is out of the way, we can concentrate on the coding. The first piece of business would be to cover how to schedule jobs and what jobs are. A job is simply a task that wants to run at any given time. This can be anything, but, the whole purpose of job scheduling is so that your app doesn't freeze when it has to do a lot of work; so the best would be to take time consuming tasks and prioritize them. You should already have an idea of which functions in your programs will take some time and which won't - you should just set the desired priorities of each job. We schedule the tasks / jobs we want to run via the Scheduler API.
A note: Seeing the fact that this article makes use of JavaScript and HTML coding and references, make sure that you are indeed up to speed with all these technologies.
Scheduling JobsScheduling work is as easy as passing the function that should do the desired task to the schedule method, and specifying the task's priority. We first need to create the job(s) and then we can set their priorities. We also pass a function to each job so that each job runs separately. This is the beauty of Asynchronous programming. Here is a small example on creating a job:
- var JS_Scheduler = WinJS.Utilities.Scheduler; //Create Scheduler Object
- function JS_Task() {
- window.output("\nScheduling job");
- //Schedule A Job
- var Job = JS_Scheduler.schedule(function () {
- window.output("Here I Can Do Whatever I Like"); //Give Some Work
- }, JS_Scheduler.Priority.normal); //Set Priority
- }
It looks more complicated than what it is! On the first line, we create the Scheduler object. This gets its capabilities from WinJS.Utilities.Scheduler. Next, we create a function named JS_Task. We then create a job. When and how this job will be run, depends on its priority, which in this case, is Normal. Here is a complete list of available Priorities.
IJob Interface
With the use of the IJob interface, we can Pause, Yield, Resume, Drain and Cancel jobs. Here is more information on it.
Pausing jobs
In your project, add a new JavaScript file by clicking on Project, Add New File and select JavaScript File from the list, as shown in Figure 2, and give it a name such as pause.js.
Inside Pause.js enter the following code:
- (function () {
- "use strict";
- //Are We On Correct WebPage?
- WinJS.UI.Pages.define("default.html", {
- ready: function (element, options) {
- document.getElementById("pause").addEventListener("click", JS_pause, false); //Add Listener
- WinJS.Navigation.addEventListener("beforenavigate", beforeNavigate);
- }
- });
- var JS_Scheduler = WinJS.Utilities.Scheduler;//Create Scheduler Object
- function beforeNavigate(eventInfo) {
- //Pause Task
- if (WinJS.Navigation.location === pageLocation) {
- JS_pause();
- }
- }
- //Function To Be Run When Cancel Button Is Clicked
- function JS_pause() {
- window.output("\nScheduling job to pause");
- // Schedule A Job To Be Paused
- var JobToBePaused = JS_Scheduler.schedule(function () {
- window.output("Here I Can Do Whatever I Like");
- }, JS_Scheduler.Priority.normal);
- var SomeOtherJob = JS_Scheduler.schedule(function () {
- window.output("Here I Can Do Some Other Stuff");
- }, JS_Scheduler.Priority.normal);
- window.output("\nI'm Tired Now, May I Take A Break?");
- JobToBePaused.pause(); //Pause Job
- }
- }
- )();
A lot happens here! First, we have to determine which page we need this script to run on. In this case, I have kept it at default.html. You could of course have added another HTML file for better organization of your code - but I'll leave that decision up to you. Then, we have to identify the button we want to use with this JavaScript code. In this case we need the pause button and we need to add a listener to it. A listener is basically the same as an event handler.
I have added another event called beforeNavigate. This event will trigger the pausing code, which is defined in the function named JS_pause.
JS_pause gives output on what we will do, then it creates the scheduler named JobToBePaused. I can let it do whatever I like, but this example just shows a simple message. I set its priority. I then create another job and pause the job I need to. Obviously this is just an example, but I think you'll get the idea.
Resuming JobsTo resume a task, you simple need to use:
- JobToBePaused.resume
Cancelling Jobs
CompletelyAdd another new JavaScript file to your project, as explained previously, name it cancel.js and enter the following code into it:
- (function () {
- "use strict";
- //Are We On Correct WebPage?
- WinJS.UI.Pages.define("default.html", {
- ready: function (element, options) {
- document.getElementById("cancel").addEventListener("click", JS_cancel, false);
- }
- });
- var JS_Scheduler = WinJS.Utilities.Scheduler; //Create Scheduler Object
- //Function To Be Run When Cancel Button Is Clicked
- function JS_cancel() {
- window.output("\nScheduling job to cancel");
- //Schedule A Job To Be Cancelled
- var JobToBeCanned = JS_Scheduler.schedule(function () {
- window.output("Here I Can Do Whatever I Like"); //Give Some Work
- }, JS_Scheduler.Priority.normal); //Set Priority
- window.output("Had Fun? OK, Good Bye, I am Being Cancelled Now!");
- JobToBeCanned.cancel(); //Cancel Job
- }
- })();
As you can see, it looks very similar to the previous code segment, we just cancel a job completely.
Yielding Jobs Within a TaskAdd a new JavaScript File, name it yield.js and enter the following:
- (function () {
- "use strict";
- //Make Sure We Have Correct Page
- WinJS.UI.Pages.define("default.html", {
- ready: function (element, options) {
- document.getElementById("yield").addEventListener("click", JS_yield, false); //Add Listener
- WinJS.Navigation.addEventListener("beforenavigate", beforeNavigate);
- }
- });
- var JS_Scheduler = WinJS.Utilities.Scheduler; //New Scheduler Object
- var Completed;
- function beforeNavigate(eventInfo) {
- if (WinJS.Navigation.location === pageLocation) {
- //Do Necessary Work To Complete All Tasks
- return;
- }
- if (eventInfo.detail.location === pageLocation) {
- Completed = false; //Task Was Not Completed
- return;
- }
- }
- function JS_yield() {
- //Function To Be Run When Yield Clicked
- JS_Scheduler.schedule(function YieldWorker(YieldJobInfo) {
- while (!Completed) {
- if (YieldJobInfo.shouldYield) {
- window.output("Yielding just temporarily"); //Wait A Bit
- YieldJobInfo.setWork(YieldWorker);
- break;
- }
- //Run Again
- else {
- window.output("Running idle yielding job again");
- var start = performance.now();
- //Do Complicated Task
- }
- }
- })}});
The trick with yielding jobs within jobs is that you need to know which part of your function might consume most of the processor and time. Once you encounter a possible long task, you could yield it temporarily until something has completed and then resume it again.
Draining Jobs
Add another JavaScript file and name it appropriately. Add the following code to it in order to drain tasks:
- (function () {
- "use strict";
- //Right Place?
- WinJS.UI.Pages.define("default.html", {
- ready: function (element, options) {
- document.getElementById("drain").addEventListener("click", JS_drain, false); //Add Listener
- }
- });
- var JS_Scheduler = WinJS.Utilities.Scheduler;
- //Function To Run When Drain Button Clicked
- function JS_drain() {
- DrainJobBasedOnPriority(JS_Scheduler.Priority.belowNormal, "belowNormal");
- }
- //Function Called By JS_drain To Drain Priority Based
- function DrainJobBasedOnPriority(JS_Priority, JS_PriorityName) {
- window.output("\nScheduling job(s) to drain");
- //Schedule Job To Not Be Drained
- JS_Scheduler.schedule(function () {
- window.output("Normal Priority Job Will Not Be Drained :)");
- }, JS_Scheduler.Priority.normal);
- //Schedule Job To Be Drained
- JS_Scheduler.schedule(function () {
- window.output("BelowNormal Priority Will Be Drained :(");
- }, JS_Scheduler.Priority.belowNormal);
- //Drain Job
- window.output("Draining " + JS_PriorityName + " Priorities");
- JS_Scheduler.requestDrain(priority).done(function () {
- window.output("Done draining! I'm Done!");
- }
- );
- }
- })();
What happens here is: we create and schedule jobs like normal. We then request to drain the functions that run under the lowest priority. Why? To clean up resources and it is simply good housekeeping to let things go when they are not really used.
If you were to run your app now, nothing will happen! Why? Because we need to let our default.html file know about all the new JavaScript files we have added. Open default.html and enter the following just above the opening Body tag:
- <script src="/js/default.js"></script>
- <script src="pause.js"></script>
- <script src="cancel.js"></script>
- <script src="yield.js"></script>
- <script src="drain.js"></script>